Skip to content

feat(store,webhook): usage metering, daily caps, and per-installation concurrency (#15)#57

Merged
BunsDev merged 1 commit into
mainfrom
feat/issue-15-metering
Jul 7, 2026
Merged

feat(store,webhook): usage metering, daily caps, and per-installation concurrency (#15)#57
BunsDev merged 1 commit into
mainfrom
feat/issue-15-metering

Conversation

@BunsDev

@BunsDev BunsDev commented Jul 7, 2026

Copy link
Copy Markdown
Member

Closes #15 — makes familiar execution meterable and boundable.

Metering

  • Store::usage(scope): task counts, completed/failed outcomes, and attempt wall-clock runtime rolled up by installation × repo × familiar — computed from the durable rows billing/support/Cave already share
  • GET /api/github/usage behind the same fail-closed tenant boundary as /tasks (Add tenant-scoped task API authentication for CovenCave and hosted clients #3): tenant tokens see only their installation, every read audited

Limits ([installations.limits], riding the #7 policy blocks)

  • max_tasks_per_day — enforced at intake over a rolling 24h window: overflow deliveries are recorded ignored:quota_exceeded in the delivery audit, answered 200 {"ignored":"quota_exceeded"}, and never become tasks
  • max_concurrent — enforced at claim: saturated installations are skipped without starving others (proven: capped installation's second task waits while another installation's task claims; capacity frees on finish)
  • Doctor rejects zero limits

Scope note

Per-container cpu/mem/disk/network limits belong to worker isolation (#5), as the issue's dependency notes.

Verification

  • Store: concurrency-cap skip/free cycle, rolling-window counts, scoped usage rollup math
  • Routes: daily cap ignores overflow + audits the reason; uncapped installations unaffected; usage endpoint fail-closed, tenant-scoped, audited
  • 204 tests green · clippy -D warnings clean · python gates green · demo 21/21

Copilot AI review requested due to automatic review settings July 7, 2026 06:29

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements Issue #15’s usage metering and installation-tier limits in the GitHub adapter, adding a tenant-scoped usage rollup endpoint plus enforcement for per-installation daily task caps (intake) and concurrency caps (claim).

Changes:

  • Add GET /api/github/usage with the same tenant boundary + audit trail semantics as the task API.
  • Enforce max_tasks_per_day at webhook intake (recording overflow deliveries as ignored:quota_exceeded) and max_concurrent at claim time (skip saturated installations without starving others).
  • Extend config schema ([installations.limits]) and store logic/tests to support these behaviors.

Reviewed changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
README.md Updates capability matrix to include usage metering + tier limits.
crates/worker/src/lib.rs Threads per-installation concurrency caps into task-claiming.
crates/webhook/src/routes.rs Adds /api/github/usage route and enforces daily intake cap with tests.
crates/webhook/Cargo.toml Adds chrono dependency for rolling 24h cutoff calculation.
crates/store/src/lib.rs Implements claim-time concurrency skipping, metering rollup, and daily-cap helper with tests.
crates/server/src/main.rs Wires the new /api/github/usage route into the server router.
crates/config/src/lib.rs Adds installation limits config, helpers, and doctor validation for zero limits.
config/example.toml Documents [installations.limits] example configuration.
Cargo.lock Locks chrono for the webhook crate dependency change.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread crates/store/src/lib.rs
Comment on lines +557 to +561
"SELECT t.installation_id, t.repo, t.familiar_id,
COUNT(DISTINCT t.id),
SUM(CASE WHEN t.state = 'completed' THEN 1 ELSE 0 END),
SUM(CASE WHEN t.state = 'failed' THEN 1 ELSE 0 END),
COALESCE(SUM(
Comment on lines +424 to +430
if let Err(e) = state
.store
.record_delivery(delivery, Routing::Ignored("quota_exceeded"))
.await
{
return storage_unavailable(e);
}
Comment on lines +413 to +417
let cutoff = (chrono::Utc::now() - chrono::Duration::hours(24)).to_rfc3339();
match state
.store
.tasks_created_since(task.installation_id, &cutoff)
.await
…ation concurrency (#15)

Closes #15.

Metering: a usage() rollup over the durable store aggregates task counts,
terminal outcomes, and attempt wall-clock runtime by installation, repo, and
familiar, served at GET /api/github/usage behind the same fail-closed tenant
boundary and api_audit trail as the task list (#3).

Limits ride the [[installations]] policy (#7) as [installations.limits]:

- max_tasks_per_day is enforced at intake: over-quota deliveries are
  recorded ignored:quota_exceeded (visible in the delivery audit), answered
  200 with the reason, and never become tasks
- max_concurrent is enforced at claim: a saturated installation's queued
  tasks are skipped without starving other installations, and capacity
  frees as its running tasks finish (the claim loop's poll timer retries)

Doctor rejects zero limits (nothing would ever run). Metering events are
the store's own rows — tasks, attempts, deliveries — so billing and support
read the same audit surface Cave does. Per-container resource limits
(cpu/mem/disk) remain with worker isolation (#5).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Val Alexander <bunsthedev@gmail.com>
@BunsDev
BunsDev force-pushed the feat/issue-15-metering branch from a7e0aaa to 23fc82f Compare July 7, 2026 06:32
@BunsDev
BunsDev merged commit 9cf4dcf into main Jul 7, 2026
1 check passed
@BunsDev
BunsDev deleted the feat/issue-15-metering branch July 7, 2026 06:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add usage metering plus tier limits and concurrency controls

2 participants